﻿using System;
using System.Collections;
using System.Text;
using System.Collections.Generic;

namespace BizElements.TemplateEngine
{
    /// <summary>
    /// Provides common operations for array and list manipulation (conversions, sets...).
    /// </summary>
    internal static class ArrayUtil
    {
        #region Find.

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type TElement.</returns>
        internal static TElement Find<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    return obj;
            }

            return default(TElement);
        }

        #endregion

        #region FindAll.

        /// <summary>
        /// Retrieves all the elements that match the conditions defined by the specified predicate.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>A list containing all the elements that match the conditions defined by the specified predicate, if found; otherwise, an empty list.</returns>
        internal static TList FindAll<TList, TElement>(IEnumerable collection, Predicate<TElement> match)
            where TList : IList, new()
        {
            TList matches = new TList();
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    matches.Add(obj);
            }

            return matches;
        }

        #endregion

        #region IsNullOrEmpty.

        /// <summary>
        /// Checks whether the specified array is a null reference or an empty array.
        /// </summary>
        /// <param name="array">Array to check.</param>
        /// <returns><b>true</b> if the provided array is <b>null</b> or empty; otherwise, false.</returns>
        internal static bool IsNullOrEmpty(Array array)
        {
            bool isNullOrEmtpy = (array == null) || (array.Length == 0);
            return isNullOrEmtpy;
        }

        /// <summary>
        /// Checks whether the specified collection is a null reference or an empty collection.
        /// </summary>
        /// <param name="collection">Collection to check.</param>
        /// <returns><b>true</b> if the provided collection is <b>null</b> or empty; otherwise, false.</returns>
        internal static bool IsNullOrEmpty(ICollection collection)
        {
            bool isNullOrEmtpy = (collection == null) || (collection.Count == 0);
            return isNullOrEmtpy;
        }

        #endregion

        #region GetRange.

        /// <summary>Creates a shallow copy of a range of elements in the source list.</summary>
        internal static List<T> GetRange<T>(List<T> list, int startAt, int maxCount)
        {
            List<T> tail;
            if (startAt >= list.Count)
            {
                tail = new List<T>();
            }
            else if (maxCount <= 0)
            {
                int count = list.Count - startAt;
                tail = list.GetRange(startAt, count);
            }
            else
            {
                int tailLength = list.Count - startAt;
                int count = Math.Min(tailLength, maxCount);
                tail = list.GetRange(startAt, count);
            }

            return tail;
        }

        /// <summary>Creates a shallow copy of a range of elements in the source list.</summary>
        internal static List<T> GetRange<T>(IEnumerable<T> list, int startAt, int maxCount)
        {
            List<T> range = new List<T>();
            int currIdx = 0;
            int currCount = 0;
            bool noLengthLimit = (maxCount <= 0);
            foreach (T item in list)
            {
                bool isIdxFromRange = (currIdx >= startAt);
                bool isCountFromRange = noLengthLimit || (currCount < maxCount);
                if (isIdxFromRange && isCountFromRange)
                {
                    range.Add(item);
                    currCount++;
                }

                currIdx++;
            }

            return range;
        }

        #endregion
        
        #region Exists.

        /// <summary>
        /// Determines whether the collection contains elements that match the conditions defined by the specified predicate.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>true if the collection contains one or more elements that match the conditions defined by the specified predicate; otherwise, false.</returns>
        internal static bool Exists<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    return true;
            }

            return false;
        }

        #endregion

        #region TryGetValue.

        /// <summary>Tries to get the value associated with the specified key.</summary>
        internal static TValue? TryGetValue<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key)
            where TValue : struct
        {
            TValue value;
            bool exists = dictionary.TryGetValue(key, out value);
            if (exists)
                return value;
            else
                return null;
        }

        #endregion
    }
}